---
title: Building Standalone Apps
---

import { Terminal } from '~/ui/components/Snippet';

> This doc was archived in August 2022 and will not receive any further updates. SDK 46 is the last SDK supported by Classic Builds and the Classic Build service will stop running for all SDK versions after January 4, 2023. Instead, use [EAS Build](/build/setup).

Classic Build service allows you to create standalone binaries for the Expo app using the Expo CLI. You can use that binary to submit the app to the Apple App Store and Google Play Store or test on emulators.

An Apple Developer account is required to build a standalone iOS app, but a Google Play Developer account is not required to build the standalone Android app. To submit to either app store, you need a developer account on that store.

> We recommend that you read the [best practices about app stores](/distribution/app-stores) to ensure your app gets accepted into the Apple and Google stores. Once you have created an amazing app, EAS Build can help you build it and then submit it to the app stores.

## 1. Install Expo CLI

Expo CLI is a command-line application required to build standalone apps. It is installed as a global npm package. If you have not installed it, go through the [installation steps in the Expo CLI documentation](/more/expo-cli/#installation).

You must have an Expo account and be logged in to create a build. When running the build command, you can create a new account or log in to an existing account.

Developers using the **Windows** operating system must have WSL enabled. If you do not have it installed, follow this [installation guide](https://docs.microsoft.com/en-us/windows/wsl/install). When it prompts for choosing a Linux distribution, we recommend picking Ubuntu from the Windows Store. Launch Ubuntu at least once and then use an admin PowerShell to run the command:

<Terminal
  cmd={['$ Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux']}
/>

## 2. Configure app.json

In **app.json**, you have to add a set of configurations before running the build command. The code snippet below represents a minimal required version of the configuration:

```json
{
  "expo": {
    "name": "Your App Name",
    "icon": "./path/to/your/app-icon.png",
    "version": "1.0.0",
    "slug": "your-app-slug",
    "ios": {
      "bundleIdentifier": "com.yourcompany.yourappname",
      "buildNumber": "1.0.0"
    },
    "android": {
      "package": "com.yourcompany.yourappname",
      "versionCode": 1
    }
  }
}
```

Here are the required fields that you have to pay attention to when configuring your **app.json** or **app.config.js** file:

- Add fields such as `name`, `icon`, and `version`.
- The iOS `bundleIdentifier` and Android `package` fields use reverse DNS notation but do not have to be related to a domain. Replace `"com.yourcompany.yourappname"` with whatever makes sense for your app.
- The `slug` should be a URL safe value. The slug will be included in the URL that your app's JavaScript is published to. For example: `expo.dev/@community/native-component-list`, where `community` is the username and `native-component-list` is the slug.
- The `ios.buildNumber` and `android.versionCode` distinguish different binaries of your app. Make sure to increment these for each build you upload to the App Store or Google Play Store.

There are other options you can add to **app.json**. For example, some developers like to configure their own build number, linking scheme, and so on. We recommend you go through [Configuration with app.json/app.config.js](/workflow/configuration) for the full specification. At this step, also check [App stores best practices](/distribution/app-stores). It provides details on what types of metadata are required for the app stores.

## 3. Start the build

To start the build process, run `expo build:android` or `expo build:ios` depending on the platform you are building for. If there is no development server running for the app you are building, `expo` will start it automatically.

> When you run `expo build`, Expo automatically publishes your app (with Classic Update's `expo publish` command). To avoid accidentally publishing changes to your production app, you may want to use [release channels](/archive/classic-updates/release-channels).

### Building for Android

There are two types of build you can generate when building a standalone app for Android: an APK or an Android App Bundle (aab).

To build an APK, run the command:

<Terminal cmd={['$ expo build:android -t apk']} />

We recommend building an Android App Bundle (aab) for submitting the app to the Google Play Store. To build an aab, run the command:

<Terminal cmd={['$ expo build:android -t app-bundle']} />

When building an aab, make sure [Google Play App Signing](/app-signing/app-credentials) is enabled for your project. You can read more about it in the [Android documentation](https://developer.android.com/guide/app-bundle).

#### Generating keystore

When building your app for the first time, you'll have to generate a [keystore](https://developer.android.com/privacy-and-security/keystore). You can upload your own if you are familiar with the process of generating keystores, or you can let Expo handle the process.

For Expo to handle the process, choose the first option in the command-line when prompted for keystore:

```sh
[exp] No currently active or previous builds for this project.

Would you like to upload a keystore or have us generate one for you?
If you don't know what this means, let us handle it! :)

1) Let Expo handle the process!
2) I want to upload my own keystore!
```

If you choose the first option, it is **strongly recommended** that you run the following command after the build process is complete. After running it, backup the keystore in a safe location:

<Terminal cmd={['$ expo fetch:android:keystore']} />

Once you submit the app to the Google Play Store, all future updates to that app **must** be signed with the same keystore that the Play Store initially accepted. If you do not have a local copy of your keystore, you will be unable to publish new versions of your app to the Play Store.

If for any reason you delete your project or clear your credentials in the future, you will not be able to submit any updates to your app without providing the same keystore. If you're unable to retrieve the keystone, your only option left is to generate a new keystore and re-upload your application as a new application.

#### Uploading your keystore after publishing the app

If you choose the first option when generating the keystore and later decide to upload your own keystore, you can use the option to clear the current Android keystore from Expo's build servers by running the command:

<Terminal cmd={['$ expo build:android --clear-credentials']} />

**This is irreversible, so only run this command if you have generated your own keystore.** You can learn more about how code signing and keystores work in the [Android documentation](https://developer.android.com/studio/publish/app-signing.html).

### Building for iOS

There are two types of build you can generate when building a standalone app for iOS: archive and simulator.

Archive builds are used for submitting your app to the Apple App Store and distributing it with features like TestFlight. To build an iOS app archive, run:

<Terminal cmd={['$ expo build:ios -t archive']} />

Simulator builds are used to test a standalone app in an iOS Simulator. To build one, run:

<Terminal cmd={['$ expo build:ios -t simulator']} />

When building for iOS, you can choose to let Expo create any necessary credentials that your app will need. However, you'll still have the choice to provide your own credentials if you'd like. During this process, Expo CLI will use your Apple ID and password. Your authentication information is only used locally on your computer and is never sent to Expo's servers.

```sh
$ expo build:ios
[16:44:37] Checking if current build exists...

[16:44:37] No currently active or previous builds for this project.
[16:44:37]
We need your Apple ID/password to manage certificates, keys
and provisioning profiles from your Apple Developer account.

Note: Expo does not keep your Apple ID or your Apple ID password.

? What\'s your Apple ID? xxx@yyy.zzz
? Password? [hidden]
✔ Authenticated with Apple Developer Portal successfully!
[16:44:46] You have 4 teams associated with your account
? Which team would you like to use? 3) ABCDEFGHIJ "John Turtle" (Individual)
✔ Ensured App ID exists on Apple Developer Portal!
[16:44:59] We do not have some credentials for you: Apple Distribution Certificate, Apple Push Notifications service key, Apple Provisioning Profile
? How would you like to upload your credentials? (Use arrow keys)
❯ Expo handles all credentials, you can still provide overrides
  I will provide all the credentials and files needed, Expo does limited validation
```

If you are unfamiliar with iOS credentials, it is recommended to let Expo handle the creation and management of credentials. To learn more about them, refer to the [App Credentials documentation](/app-signing/app-credentials).

If you are familiar with iOS credentials and plan to provide your own, use the [Apple Developer Portal](https://developer.apple.com/account/resources/certificates/list) to create them.

#### Enterprise distribution

To build and distribute your app using Apple's Enterprise distribution, it is required that you are a member of the [Apple Developer Enterprise Program](https://developer.apple.com/programs/enterprise/).

Expo's build service supports Enterprise distribution. When you run `expo build:ios`, you'll need to choose the correct team that is labeled with `(In-House)`.

#### Adhoc distribution

> This distribution method requires you to override your own credentials. Use this distribution if you know how to generate iOS credentials.

One of the easiest ways to test your standalone app is using a [simulator build](#building-for-ios) and then using TestFlight for your physical device for testing. However, if you want to build and install an app directly on a physical device through Xcode, follow the steps below to generate one using Expo CLI:

- Generate preliminary certificates such as app identifier and distribution certificate by running the command:

<Terminal cmd={['$ expo build:ios']} />

- Now, create a new [provisioning profile](https://developer.apple.com/account/resources/profiles/list), and under `Distribution`, select `Adhoc`.
- Provide the app identifier and the distribution certificate you created in the first step. **If you don't provide those exact certificates, your build will fail.**
- Download the provisioning profile, and then pass it to the build command:

<Terminal
  cmd={[
    '$ expo build:ios --provisioning-profile-path ~/Path/To/provisioningProfileYouCreated.mobileprovision',
  ]}
/>

- Make sure the other certificates you use match the ones you selected when creating your adhoc provisioning profile in the previous step.

After Expo finishes your build, you can install it onto your device via Xcode.

- Open the `Devices & Simulators` window in Xcode
- Select your connected device
- Under `Installed Apps`, click the plus (`+`) and then select the IPA generated by Expo

> **Note:** We enable bitcode for iOS, so the **.ipa** files for iOS are much larger than the eventual App Store download available to your users. For more information, see [App Thinning](https://help.apple.com/xcode/mac/current/#/devbbdc5ce4f).

### Switch to Push Notification Key on iOS

If you use a Push Notifications Certificate and want to switch to a [Push Notifications Key](/app-signing/app-credentials/#push-notification-keys/), start the build with `--clear-push-cert` option. Expo will remove legacy certificates from its servers and generate a Push Notifications Key for you.

## 4. Wait for it to finish building

Expo's build service uses cloud services to build your app. The build process starts as soon as one of the machines is free and ready to be used. To check how long the waiting time for your build is, visit [Turtle status](https://expo.dev/turtle-status) site.

Expo CLI prints a URL that you can visit and check your build for its progress and logs.

Alternatively, you can check the status by running:

<Terminal cmd={['$ expo build:status']} />

When the build process finishes, you will get a URL to your app file - an **.apk**, **.aab** (both Android), or **.ipa** (iOS) file. Copy and paste the link into your browser to download the file.

> Want to be notified programmatically as soon as your build is done? [Here's how you can set that up with webhooks](/eas/webhooks).

## 5. Test it on your device or simulator

### Android

- **To run it on your Android Emulator**, first build your project with the apk flag by running `expo build:android -t apk`, and drag and drop the **.apk** into the emulator.
- **To run it on your Android device**, make sure you have the Android platform tools installed along with `adb`, then just run `adb install app-filename.apk` with [USB debugging enabled on your device](https://developer.android.com/studio/run/device#device-developer-options) and the device plugged in.

### iOS

- **To run it on your iOS Simulator**, first build your project with the simulator flag by running `expo build:ios -t simulator`, then download the artifact with the link printed when your build completes. To install the resulting **tar.gz** file, unzip it and drag and drop it into your iOS Simulator. If you'd like to install it from the command line, run `tar -xvzf your-app.tar.gz` to unpack the file, open a simulator, then run `xcrun simctl install booted <path to .app>`.
- **To test a device build with Apple TestFlight**, download the **.ipa** file to your local machine. Within [App Store Connect](https://appstoreconnect.apple.com/apps), click the plus icon and create a New App. Make sure your `bundleIdentifier` matches what you have placed in **app.json**. Now, you need to use Xcode or [Transporter](https://apps.apple.com/app/transporter/id1450874784) (previously known as Application Loader) to upload the **.ipa** you got from `expo build:ios`. Once you do that, you can check the status of your build under `Activity`. Processing an app can take 10-15 minutes before it shows up under available builds.

## 6. Submit it to the appropriate store

Read the documentation on [Uploading Apps to the Apple App Store and Google Play](/submit/introduction).

## 7. Update your app

To update your app, use the Classic Update's `expo publish` command from the Expo CLI. The physical device on which the app is installed will download the new JavaScript update the next time the app is open on that device. To ensure your app users have a seamless experience downloading JavaScript updates, you may want to enable [background JS downloads](/archive/classic-updates/offline-support).

There are a couple of reasons why you might want to rebuild and resubmit the native binaries:

- If you want to change native metadata like the app's name or icon
- If you upgrade to a newer SDK version of the app (which requires new native code)

You have to update the app's `versionCode` and `buildNumber` in **app.json**. Read the [Versioning Your app documentation](/distribution/app-stores#versioning-your-app) for more details.

We also recommend you go through [app config documentation](/workflow/configuration) to get an idea of all the properties you can change. Such as the icons, deep linking URL scheme, add or remove handset/tablet support, and so on.
